package cn.org.rookie.jeesdp.core;

/**
 * Twitter的SnowFlake算法实现，用于生成分布式环境下的唯一ID
 * <p>
 * ID结构：时间戳(41位) + 数据中心ID(5位) + 机器ID(5位) + 序列号(12位)
 * <p>
 * 优化内容：
 * 1. 添加更合理的起始时间戳（2025-01-01）
 * 2. 修复注释中的拼写错误
 * 3. 优化代码结构和可读性
 * 4. 改进异常信息
 * 5. 优化位运算性能
 */
public class Snowflake {

    /**
     * 起始时间戳 (2025-01-01 00:00:00 UTC)
     * 使用未来的时间戳可以确保生成的ID在一段时间内不会重复
     */
    private final static long START_TIMESTAMP = 1735689600000L;

    /**
     * 序列号占用的位数
     * 每毫秒最多可以生成2^12=4096个ID
     */
    private final static long SEQUENCE_BIT = 12;

    /**
     * 机器标识占用的位数
     * 最多支持2^5=32台机器
     */
    private final static long MACHINE_BIT = 5;

    /**
     * 数据中心占用的位数
     * 最多支持2^5=32个数据中心
     */
    private final static long DATA_CENTER_BIT = 5;

    /**
     * 序列号的最大值
     */
    private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);

    /**
     * 机器ID的最大值
     */
    private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);

    /**
     * 数据中心ID的最大值
     */
    private final static long MAX_DATA_CENTER_NUM = ~(-1L << DATA_CENTER_BIT);

    /**
     * 机器ID需要左移的位数
     */
    private final static long MACHINE_LEFT = SEQUENCE_BIT;

    /**
     * 数据中心ID需要左移的位数
     */
    private final static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;

    /**
     * 时间戳需要左移的位数
     */
    private final static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT;

    /**
     * 数据中心ID
     */
    private final long dataCenterId;

    /**
     * 机器ID
     */
    private final long machineId;

    /**
     * 当前毫秒内的序列号
     */
    private long sequence = 0L;

    /**
     * 上一次生成ID的时间戳
     */
    private long lastTimeStamp = -1L;

    /**
     * 构造函数，初始化数据中心ID和机器ID
     *
     * @param dataCenterId 数据中心ID，取值范围[0,31]
     * @param machineId    机器ID，取值范围[0,31]
     * @throws IllegalArgumentException 当数据中心ID或机器ID超出范围时抛出
     */
    public Snowflake(long dataCenterId, long machineId) {
        if (dataCenterId > MAX_DATA_CENTER_NUM || dataCenterId < 0) {
            throw new IllegalArgumentException("DataCenterId can't be greater than " + MAX_DATA_CENTER_NUM + " or less than 0!");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("MachineId can't be greater than " + MAX_MACHINE_NUM + " or less than 0!");
        }
        this.dataCenterId = dataCenterId;
        this.machineId = machineId;
    }

    /**
     * 获取下一个时间戳，确保时间戳是递增的
     * 当系统时间回退时，该方法会等待直到获取到新的时间戳
     *
     * @return 下一个时间戳
     */
    private long getNextMill() {
        long mill = getNewTimeStamp();
        while (mill <= lastTimeStamp) {
            mill = getNewTimeStamp();
        }
        return mill;
    }

    /**
     * 获取当前时间戳
     *
     * @return 当前时间戳
     */
    private long getNewTimeStamp() {
        return System.currentTimeMillis();
    }

    /**
     * 生成下一个唯一ID
     *
     * @return 生成的64位唯一ID
     * @throws RuntimeException 当检测到时钟回退时抛出
     */
    public synchronized long nextId() {
        long currTimeStamp = getNewTimeStamp();

        // 时钟回退检查
        if (currTimeStamp < lastTimeStamp) {
            throw new RuntimeException("Clock moved backwards. Refusing to generate id!");
        }

        // 同一毫秒内，序列号自增
        if (currTimeStamp == lastTimeStamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            // 同一毫秒的序列数已经达到最大，等待下一毫秒
            if (sequence == 0L) {
                currTimeStamp = getNextMill();
            }
        } else {
            // 不同毫秒内，序列号置为0
            sequence = 0L;
        }

        lastTimeStamp = currTimeStamp;

        // 使用位运算组合各部分，提高可读性
        long timestampPart = (currTimeStamp - START_TIMESTAMP) << TIMESTAMP_LEFT;
        long dataCenterPart = dataCenterId << DATA_CENTER_LEFT;
        long machinePart = machineId << MACHINE_LEFT;
        long sequencePart = sequence;

        return timestampPart | dataCenterPart | machinePart | sequencePart;
    }
}
